"""
Complete example demonstrating the usage of the Python Backend Toolkit.
This example shows how to use all the modules together to build a robust backend application.
"""

import asyncio
import os
from datetime import datetime, timedelta
from typing import List, Optional

from fastapi import FastAPI, Depends, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, EmailStr
from sqlalchemy import Column, String, Integer, Boolean, Text
from sqlalchemy.orm import Session

# Import our toolkit modules
from python_backend_toolkit.database import (
    Base, BaseModel as DBBaseModel, get_db, 
    BaseRepository, BaseService, DatabaseSession, DatabaseConfig
)
from python_backend_toolkit.auth import (
    User, UserCreate, UserResponse, Token,
    AuthService, JWTHandler, PasswordHandler
)
from python_backend_toolkit.api import (
    BaseAPI, CRUDBaseAPI, AuthenticatedAPI,
    APIMiddleware, RateLimitMiddleware, SecurityHeadersMiddleware,
    SuccessResponse, ErrorResponse, CreatedResponse, ListResponse,
    APIException, ValidationError, NotFoundError, UnauthorizedError
)
from python_backend_toolkit.cache import (
    RedisCache, CacheService, cached, cache_key
)
from python_backend_toolkit.logging import (
    Logger, get_logger, LoggingMiddleware
)
from python_backend_toolkit.utils import (
    EmailValidator, PasswordValidator,
    generate_uuid, generate_slug,
    retry, rate_limit, measure_time,
    Paginator, PaginationParams
)
from python_backend_toolkit.http_client import AsyncHTTPClient


# ============================================================================
# 1. Database Models
# ============================================================================

class Post(DBBaseModel):
    """Blog post model."""
    
    title = Column(String(200), nullable=False, index=True)
    slug = Column(String(250), nullable=False, unique=True, index=True)
    content = Column(Text, nullable=False)
    author_id = Column(Integer, nullable=False, index=True)
    is_published = Column(Boolean, default=False, nullable=False)
    view_count = Column(Integer, default=0, nullable=False)


class Comment(DBBaseModel):
    """Comment model."""
    
    content = Column(Text, nullable=False)
    post_id = Column(Integer, nullable=False, index=True)
    author_id = Column(Integer, nullable=False, index=True)
    is_approved = Column(Boolean, default=False, nullable=False)


# ============================================================================
# 2. Pydantic Schemas
# ============================================================================

class PostCreate(BaseModel):
    title: str
    content: str
    is_published: bool = False


class PostUpdate(BaseModel):
    title: Optional[str] = None
    content: Optional[str] = None
    is_published: Optional[bool] = None


class PostResponse(BaseModel):
    id: int
    title: str
    slug: str
    content: str
    author_id: int
    is_published: bool
    view_count: int
    created_at: datetime
    updated_at: datetime
    
    class Config:
        from_attributes = True


class CommentCreate(BaseModel):
    content: str
    post_id: int


class CommentResponse(BaseModel):
    id: int
    content: str
    post_id: int
    author_id: int
    is_approved: bool
    created_at: datetime
    
    class Config:
        from_attributes = True


# ============================================================================
# 3. Repository Layer
# ============================================================================

class PostRepository(BaseRepository[Post]):
    """Post repository with custom methods."""
    
    def get_published_posts(self, skip: int = 0, limit: int = 100) -> List[Post]:
        """Get published posts only."""
        return (
            self.db.query(self.model)
            .filter(self.model.is_published == True)
            .filter(self.model.is_active == True)
            .offset(skip)
            .limit(limit)
            .all()
        )
    
    def get_by_slug(self, slug: str) -> Optional[Post]:
        """Get post by slug."""
        return (
            self.db.query(self.model)
            .filter(self.model.slug == slug)
            .filter(self.model.is_active == True)
            .first()
        )
    
    def increment_view_count(self, post_id: int) -> bool:
        """Increment post view count."""
        try:
            self.db.query(self.model).filter(
                self.model.id == post_id
            ).update({
                "view_count": self.model.view_count + 1
            })
            self.db.commit()
            return True
        except Exception:
            self.db.rollback()
            return False


class CommentRepository(BaseRepository[Comment]):
    """Comment repository with custom methods."""
    
    def get_by_post_id(self, post_id: int, approved_only: bool = True) -> List[Comment]:
        """Get comments for a post."""
        query = self.db.query(self.model).filter(self.model.post_id == post_id)
        
        if approved_only:
            query = query.filter(self.model.is_approved == True)
        
        return query.filter(self.model.is_active == True).all()


# ============================================================================
# 4. Service Layer
# ============================================================================

class PostService(BaseService[Post]):
    """Post service with business logic."""
    
    def __init__(self, repository: PostRepository):
        super().__init__(repository)
        self.email_validator = EmailValidator()
        self.password_validator = PasswordValidator()
    
    def create_post(self, post_data: PostCreate, author_id: int) -> Post:
        """Create a new post."""
        # Generate slug from title
        slug = generate_slug(post_data.title)
        
        # Ensure slug is unique
        counter = 1
        original_slug = slug
        while self.repository.get_by_slug(slug):
            slug = f"{original_slug}-{counter}"
            counter += 1
        
        # Create post data
        create_data = {
            **post_data.dict(),
            "slug": slug,
            "author_id": author_id
        }
        
        return self.create(create_data)
    
    @cached(expire=300, key_func=lambda self, slug: f"post:slug:{slug}")
    async def get_by_slug_cached(self, slug: str) -> Optional[Post]:
        """Get post by slug with caching."""
        return self.repository.get_by_slug(slug)
    
    @measure_time
    async def get_published_posts_paginated(
        self, 
        page: int = 1, 
        size: int = 20
    ) -> dict:
        """Get published posts with pagination and caching."""
        cache_key_str = f"published_posts:{page}:{size}"
        
        # Try cache first
        cached_result = await self._get_from_cache(cache_key_str)
        if cached_result:
            return cached_result
        
        # Get from database
        skip = (page - 1) * size
        posts = self.repository.get_published_posts(skip=skip, limit=size)
        total = self.repository.count({"is_published": True, "is_active": True})
        
        result = {
            "items": posts,
            "total": total,
            "page": page,
            "size": size,
            "pages": (total + size - 1) // size,
            "has_next": page * size < total,
            "has_prev": page > 1
        }
        
        # Cache result
        await self._set_cache(cache_key_str, result, ttl=60)
        
        return result
    
    async def increment_view_count(self, post_id: int) -> bool:
        """Increment view count with cache invalidation."""
        success = self.repository.increment_view_count(post_id)
        
        if success:
            # Invalidate cache for this post
            await self._delete_cache(self._get_cache_key("id", post_id))
        
        return success


class CommentService(BaseService[Comment]):
    """Comment service with moderation logic."""
    
    def create_comment(self, comment_data: CommentCreate, author_id: int) -> Comment:
        """Create a new comment."""
        create_data = {
            **comment_data.dict(),
            "author_id": author_id,
            "is_approved": False  # Comments need approval
        }
        
        return self.create(create_data)
    
    def approve_comment(self, comment_id: int) -> bool:
        """Approve a comment."""
        comment = self.get_by_id_sync(comment_id)
        if not comment:
            return False
        
        updated_comment = self.update(comment_id, {"is_approved": True})
        return updated_comment is not None
    
    @cached(expire=180, key_func=lambda self, post_id: f"comments:post:{post_id}")
    async def get_approved_comments_for_post(self, post_id: int) -> List[Comment]:
        """Get approved comments for a post with caching."""
        return self.repository.get_by_post_id(post_id, approved_only=True)


# ============================================================================
# 5. API Layer
# ============================================================================

class PostAPI(CRUDBaseAPI):
    """Post API endpoints."""
    
    def __init__(self):
        super().__init__(Post, PostService)
        self.logger = get_logger("PostAPI")
    
    def get_service(self, db: Session) -> PostService:
        """Get post service instance."""
        repository = PostRepository(Post, db)
        return PostService(repository)


class CommentAPI(AuthenticatedAPI):
    """Comment API endpoints."""
    
    def __init__(self):
        super().__init__()
        self.logger = get_logger("CommentAPI")
    
    def get_service(self, db: Session) -> CommentService:
        """Get comment service instance."""
        repository = CommentRepository(Comment, db)
        return CommentService(repository)


# ============================================================================
# 6. FastAPI Application Setup
# ============================================================================

def create_app() -> FastAPI:
    """Create and configure FastAPI application."""
    
    # Initialize database
    db_config = DatabaseConfig(
        database_url=os.getenv("DATABASE_URL", "sqlite:///./blog.db"),
        echo=os.getenv("DEBUG", "false").lower() == "true"
    )
    db_session = DatabaseSession(db_config)
    db_session.create_tables()
    
    # Create FastAPI app
    app = FastAPI(
        title="Blog API",
        description="A blog API built with Python Backend Toolkit",
        version="1.0.0"
    )
    
    # Add middleware
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    app.add_middleware(SecurityHeadersMiddleware)
    app.add_middleware(RateLimitMiddleware, requests_per_minute=100)
    app.add_middleware(APIMiddleware)
    app.add_middleware(LoggingMiddleware)
    
    # Initialize APIs
    post_api = PostAPI()
    comment_api = CommentAPI()
    
    # ========================================================================
    # Authentication Endpoints
    # ========================================================================
    
    @app.post("/auth/register", response_model=SuccessResponse[UserResponse])
    async def register(
        user_data: UserCreate,
        request: Request,
        db: Session = Depends(get_db)
    ):
        """Register a new user."""
        try:
            post_api.log_request(request)
            
            # Create user
            auth_service = AuthService()
            user = await auth_service.create_user(user_data.dict(), db)
            
            response = CreatedResponse.create(
                data=UserResponse.from_orm(user),
                message="User registered successfully",
                request_id=post_api.get_request_id(request)
            )
            
            post_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return post_api.handle_exception(request, exc)
    
    @app.post("/auth/login", response_model=SuccessResponse[Token])
    async def login(
        username: str,
        password: str,
        request: Request,
        db: Session = Depends(get_db)
    ):
        """Login user."""
        try:
            post_api.log_request(request)
            
            auth_service = AuthService()
            token = await auth_service.authenticate_user(username, password, db)
            
            if not token:
                raise UnauthorizedError("Invalid credentials")
            
            response = SuccessResponse.create(
                data=token,
                message="Login successful",
                request_id=post_api.get_request_id(request)
            )
            
            post_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return post_api.handle_exception(request, exc)
    
    # ========================================================================
    # Post Endpoints
    # ========================================================================
    
    @app.get("/posts", response_model=SuccessResponse[List[PostResponse]])
    async def get_posts(
        request: Request,
        page: int = 1,
        size: int = 20,
        db: Session = Depends(get_db)
    ):
        """Get published posts with pagination."""
        try:
            post_api.log_request(request, page=page, size=size)
            
            # Validate pagination
            pagination = post_api.validate_pagination_params(page, size)
            
            service = post_api.get_service(db)
            result = await service.get_published_posts_paginated(
                page=pagination["page"],
                size=pagination["size"]
            )
            
            # Convert to response format
            posts_response = [PostResponse.from_orm(post) for post in result["items"]]
            
            response = ListResponse.create(
                items=posts_response,
                message="Posts retrieved successfully",
                request_id=post_api.get_request_id(request)
            )
            
            # Add pagination metadata
            response.total = result["total"]
            response.page = result["page"]
            response.pages = result["pages"]
            response.has_next = result["has_next"]
            response.has_prev = result["has_prev"]
            
            post_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return post_api.handle_exception(request, exc)
    
    @app.get("/posts/{slug}", response_model=SuccessResponse[PostResponse])
    async def get_post_by_slug(
        slug: str,
        request: Request,
        db: Session = Depends(get_db)
    ):
        """Get post by slug."""
        try:
            post_api.log_request(request, slug=slug)
            
            service = post_api.get_service(db)
            post = await service.get_by_slug_cached(slug)
            
            if not post:
                raise NotFoundError(
                    detail="Post not found",
                    resource_type="Post",
                    resource_id=slug
                )
            
            # Increment view count asynchronously
            asyncio.create_task(service.increment_view_count(post.id))
            
            response = SuccessResponse.create(
                data=PostResponse.from_orm(post),
                message="Post retrieved successfully",
                request_id=post_api.get_request_id(request)
            )
            
            post_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return post_api.handle_exception(request, exc)
    
    @app.post("/posts", response_model=CreatedResponse[PostResponse])
    async def create_post(
        post_data: PostCreate,
        request: Request,
        current_user: User = Depends(comment_api.get_current_user_dependency()),
        db: Session = Depends(get_db)
    ):
        """Create a new post."""
        try:
            post_api.log_request(request, user_id=current_user.id)
            
            service = post_api.get_service(db)
            post = service.create_post(post_data, current_user.id)
            
            response = CreatedResponse.create(
                data=PostResponse.from_orm(post),
                message="Post created successfully",
                request_id=post_api.get_request_id(request)
            )
            
            post_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return post_api.handle_exception(request, exc)
    
    # ========================================================================
    # Comment Endpoints
    # ========================================================================
    
    @app.get("/posts/{post_id}/comments", response_model=SuccessResponse[List[CommentResponse]])
    async def get_post_comments(
        post_id: int,
        request: Request,
        db: Session = Depends(get_db)
    ):
        """Get approved comments for a post."""
        try:
            comment_api.log_request(request, post_id=post_id)
            
            service = comment_api.get_service(db)
            comments = await service.get_approved_comments_for_post(post_id)
            
            comments_response = [CommentResponse.from_orm(comment) for comment in comments]
            
            response = ListResponse.create(
                items=comments_response,
                message="Comments retrieved successfully",
                request_id=comment_api.get_request_id(request)
            )
            
            comment_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return comment_api.handle_exception(request, exc)
    
    @app.post("/posts/{post_id}/comments", response_model=CreatedResponse[CommentResponse])
    async def create_comment(
        post_id: int,
        comment_data: CommentCreate,
        request: Request,
        current_user: User = Depends(comment_api.get_current_user_dependency()),
        db: Session = Depends(get_db)
    ):
        """Create a new comment."""
        try:
            comment_api.log_request(request, post_id=post_id, user_id=current_user.id)
            
            # Validate post exists
            post_service = post_api.get_service(db)
            if not post_service.exists(post_id):
                raise NotFoundError(
                    detail="Post not found",
                    resource_type="Post",
                    resource_id=str(post_id)
                )
            
            # Ensure comment is for the correct post
            comment_data.post_id = post_id
            
            service = comment_api.get_service(db)
            comment = service.create_comment(comment_data, current_user.id)
            
            response = CreatedResponse.create(
                data=CommentResponse.from_orm(comment),
                message="Comment created successfully (pending approval)",
                request_id=comment_api.get_request_id(request)
            )
            
            comment_api.log_response(request, response)
            return response
            
        except Exception as exc:
            return comment_api.handle_exception(request, exc)
    
    # ========================================================================
    # Health Check Endpoint
    # ========================================================================
    
    @app.get("/health")
    async def health_check():
        """Health check endpoint."""
        return {
            "status": "healthy",
            "timestamp": datetime.utcnow().isoformat(),
            "version": "1.0.0"
        }
    
    return app


# ============================================================================
# 7. Usage Examples
# ============================================================================

async def example_usage():
    """Example of how to use the toolkit components."""
    
    print("=== Python Backend Toolkit Usage Examples ===\\n")
    
    # 1. Database Operations
    print("1. Database Operations:")
    db_config = DatabaseConfig(database_url="sqlite:///./example.db")
    db_session = DatabaseSession(db_config)
    
    with db_session.session_scope() as db:
        # Create repository and service
        post_repo = PostRepository(Post, db)
        post_service = PostService(post_repo)
        
        # Create a post
        post_data = PostCreate(
            title="My First Post",
            content="This is the content of my first post.",
            is_published=True
        )
        
        post = post_service.create_post(post_data, author_id=1)
        print(f"Created post: {post.title} (ID: {post.id})")
    
    # 2. Caching Operations
    print("\\n2. Caching Operations:")
    
    @cached(expire=60, key_func=lambda x: f"example:{x}")
    async def expensive_operation(value: str) -> str:
        """Simulate expensive operation."""
        await asyncio.sleep(0.1)  # Simulate delay
        return f"Processed: {value}"
    
    # First call - will be cached
    result1 = await expensive_operation("test")
    print(f"First call result: {result1}")
    
    # Second call - will use cache
    result2 = await expensive_operation("test")
    print(f"Second call result: {result2}")
    
    # 3. HTTP Client Usage
    print("\\n3. HTTP Client Usage:")
    async with AsyncHTTPClient() as client:
        try:
            response = await client.get("https://httpbin.org/json")
            print(f"HTTP Response: {response.status_code}")
            print(f"Response data: {response.json}")
        except Exception as e:
            print(f"HTTP request failed: {e}")
    
    # 4. Validation Examples
    print("\\n4. Validation Examples:")
    email_validator = EmailValidator()
    password_validator = PasswordValidator()
    
    # Email validation
    valid_email = email_validator.validate("user@example.com")
    print(f"Email validation result: {valid_email}")
    
    # Password validation
    password_result = password_validator.validate("SecurePassword123!")
    print(f"Password validation result: {password_result}")
    
    # 5. Utility Functions
    print("\\n5. Utility Functions:")
    
    # Generate UUID
    uuid = generate_uuid()
    print(f"Generated UUID: {uuid}")
    
    # Generate slug
    slug = generate_slug("This is a Sample Title!")
    print(f"Generated slug: {slug}")
    
    # Retry decorator example
    @retry(max_attempts=3, delay=1.0)
    async def unreliable_function():
        """Function that might fail."""
        import random
        if random.random() < 0.7:  # 70% chance of failure
            raise Exception("Random failure")
        return "Success!"
    
    try:
        result = await unreliable_function()
        print(f"Retry example result: {result}")
    except Exception as e:
        print(f"Retry example failed: {e}")
    
    # 6. Pagination Example
    print("\\n6. Pagination Example:")
    paginator = Paginator()
    
    # Sample data
    sample_data = list(range(1, 101))  # 100 items
    
    # Paginate data
    paginated_result = paginator.paginate_list(
        items=sample_data,
        page=2,
        size=10
    )
    
    print(f"Page 2 items: {paginated_result.items}")
    print(f"Total items: {paginated_result.total}")
    print(f"Has next page: {paginated_result.has_next}")
    
    print("\\n=== Examples completed ===")


# ============================================================================
# 8. Main Application Entry Point
# ============================================================================

if __name__ == "__main__":
    import uvicorn
    
    # Run examples
    print("Running toolkit examples...")
    asyncio.run(example_usage())
    
    print("\\nStarting FastAPI application...")
    
    # Create and run the app
    app = create_app()
    
    # Run with uvicorn
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8000,
        reload=True,
        log_level="info"
    )


# ============================================================================
# 9. Additional Usage Examples
# ============================================================================

class AdvancedExamples:
    """Advanced usage examples for the toolkit."""
    
    @staticmethod
    async def rate_limiting_example():
        """Example of rate limiting."""
        
        @rate_limit(calls=5, period=60)  # 5 calls per minute
        async def limited_function(value: str) -> str:
            return f"Processed: {value}"
        
        # This will work for the first 5 calls
        for i in range(7):
            try:
                result = await limited_function(f"request_{i}")
                print(f"Call {i}: {result}")
            except Exception as e:
                print(f"Call {i} failed: {e}")
    
    @staticmethod
    async def circuit_breaker_example():
        """Example of circuit breaker pattern."""
        from python_backend_toolkit.utils.decorators import circuit_breaker
        
        @circuit_breaker(failure_threshold=3, recovery_timeout=30)
        async def unreliable_service():
            """Simulate an unreliable external service."""
            import random
            if random.random() < 0.8:  # 80% failure rate
                raise Exception("Service unavailable")
            return "Service response"
        
        # Test circuit breaker
        for i in range(10):
            try:
                result = await unreliable_service()
                print(f"Attempt {i}: {result}")
            except Exception as e:
                print(f"Attempt {i} failed: {e}")
            
            await asyncio.sleep(1)
    
    @staticmethod
    def logging_example():
        """Example of structured logging."""
        logger = get_logger("ExampleLogger")
        
        # Different log levels
        logger.info("Application started", version="1.0.0", environment="development")
        logger.warning("This is a warning", user_id=123, action="login_attempt")
        logger.error("An error occurred", error_code="AUTH_001", details={"reason": "invalid_token"})
        
        # HTTP request logging
        logger.log_http_request(
            method="POST",
            url="/api/users",
            status_code=201,
            duration=0.15,
            user_id=123
        )
        
        # Database query logging
        logger.log_db_query(
            query="SELECT * FROM users WHERE id = ?",
            params=[123],
            duration=0.05,
            rows_affected=1
        )
    
    @staticmethod
    async def cache_patterns_example():
        """Example of different caching patterns."""
        from python_backend_toolkit.cache import cache_result, cache_user_data
        
        # Result caching
        @cache_result(expire=300)
        async def expensive_calculation(x: int, y: int) -> int:
            await asyncio.sleep(1)  # Simulate expensive operation
            return x * y + x ** y
        
        # User data caching
        @cache_user_data(expire=600)
        async def get_user_profile(user_id: int) -> dict:
            # Simulate database query
            await asyncio.sleep(0.1)
            return {
                "id": user_id,
                "name": f"User {user_id}",
                "email": f"user{user_id}@example.com"
            }
        
        # Test caching
        result1 = await expensive_calculation(5, 3)
        result2 = await expensive_calculation(5, 3)  # This will use cache
        
        profile1 = await get_user_profile(123)
        profile2 = await get_user_profile(123)  # This will use cache
        
        print(f"Calculation results: {result1}, {result2}")
        print(f"Profile results: {profile1}, {profile2}")


# Example configuration for production
PRODUCTION_CONFIG = {
    "database": {
        "url": "postgresql://user:password@localhost/blog_db",
        "pool_size": 20,
        "max_overflow": 30,
        "echo": False
    },
    "redis": {
        "host": "localhost",
        "port": 6379,
        "db": 0,
        "password": None
    },
    "auth": {
        "secret_key": "your-secret-key-here",
        "algorithm": "HS256",
        "access_token_expire_minutes": 30
    },
    "logging": {
        "level": "INFO",
        "format": "json",
        "file": "/var/log/blog-api.log"
    },
    "api": {
        "rate_limit": 1000,  # requests per minute
        "cors_origins": ["https://yourdomain.com"],
        "max_request_size": 10 * 1024 * 1024  # 10MB
    }
}

# Example Docker Compose configuration
DOCKER_COMPOSE_EXAMPLE = """
version: '3.8'

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/blog_db
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=your-secret-key-here
    depends_on:
      - db
      - redis
    volumes:
      - ./logs:/var/log

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=blog_db
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:6-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:
"""

# Example Dockerfile
DOCKERFILE_EXAMPLE = """
FROM python:3.11-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \\
    gcc \\
    && rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Create logs directory
RUN mkdir -p /var/log

# Expose port
EXPOSE 8000

# Run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
"""

# Example requirements.txt
REQUIREMENTS_EXAMPLE = """
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9
redis==5.0.1
pydantic[email]==2.5.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
structlog==23.2.0
httpx==0.25.2
python-multipart==0.0.6
"""